package com.gitee.wsl.collections.iterator

import com.gitee.wsl.base.Objects


/**
 * Decorates an iterator to support one-element lookahead while iterating.
 *
 *
 * The decorator supports the removal operation, but an [IllegalStateException] will be thrown if [.remove] is called directly after a call to
 * [.peek] or [.element].
 *
 *
 * @param <E> the type of elements returned by this iterator.
 * @since 4.0
</E> */
class PeekingIterator<E>(
    /** The iterator being decorated.  */
    private val iterator: Iterator<E>
) : Iterator<E> {
    /** Indicates that the decorated iterator is exhausted.  */
    private var exhausted = false

    /** Indicates if the lookahead slot is filled.  */
    private var slotFilled = false

    /** The current slot for lookahead.  */
    private var slot: E? = null

    /**
     * Returns the next element in iteration without advancing the underlying iterator. If the iterator is already exhausted, null will be returned.
     *
     *
     * Note that if the underlying iterator is a [FilterIterator] or a [FilterListIterator], the underlying predicate will *not* be tested if
     * element() or [.peek] has been called after the most recent invocation of [.next]
     *
     *
     * @return the next element from the iterator
     * @throws NoSuchElementException if the iterator is already exhausted according to [.hasNext]
     */
    fun element(): E? {
        fill()
        if (exhausted) {
            throw NoSuchElementException()
        }
        return slot
    }

    private fun fill() {
        if (exhausted || slotFilled) {
            return
        }
        if (iterator.hasNext()) {
            slot = iterator.next()
            slotFilled = true
        } else {
            exhausted = true
            slot = null
            slotFilled = false
        }
    }

     override fun hasNext(): Boolean {
        if (exhausted) {
            return false
        }
        return slotFilled || iterator.hasNext()
    }

    /**
     * Returns the next element in iteration.
     *
     *
     * Note that if the underlying iterator is a [FilterIterator] or a [FilterListIterator], the underlying predicate will *not* be tested if
     * [.element] or [.peek] has been called after the most recent invocation of [.next].
     *
     *
     * @return the next element from the iterator
     * @throws NoSuchElementException if the iterator is already exhausted according to [.hasNext].
     */
    override fun next(): E {
        if (!hasNext()) {
            throw NoSuchElementException()
        }
        val x = if (slotFilled) slot else iterator.next()
        // reset the lookahead slot
        slot = null
        slotFilled = false
        return x!!
    }

    /**
     * Returns the next element in iteration without advancing the underlying iterator. If the iterator is already exhausted, null will be returned.
     *
     *
     * Note: this method does not throw a [NoSuchElementException] if the iterator is already exhausted. If you want such a behavior, use
     * [.element] instead.
     *
     *
     *
     * The rationale behind this is to follow the [java.util.Queue] interface which uses the same terminology.
     *
     *
     *
     * Note that if the underlying iterator is a [FilterIterator] or a [FilterListIterator], the underlying predicate will *not* be tested if
     * [.element] or peek() has been called after the most recent invocation of [.next].
     *
     *
     * @return the next element from the iterator
     */
    fun peek(): E? {
        fill()
        return if (exhausted) null else slot
    }

    /**
     * {@inheritDoc}
     *
     * @throws IllegalStateException if [.peek] or [.element] has been called prior to the call to [.remove].
     */
//    fun remove() {
//        check(!slotFilled) { "peek() or element() called before remove()" }
//        iterator.remove()
//    }

    companion object {
        /**
         * Decorates the specified iterator to support one-element lookahead.
         *
         *
         * If the iterator is already a [PeekingIterator] it is returned directly.
         *
         *
         * @param <E>      the element type
         * @param iterator the iterator to decorate
         * @return a new peeking iterator
         * @throws NullPointerException if the iterator is null
        </E> */
        fun <E> peekingIterator(iterator: Iterator< E>): PeekingIterator<E> {
            if (iterator is PeekingIterator<*>) {
                val it = iterator as PeekingIterator<E>
                return it
            }
            return PeekingIterator(iterator)
        }
    }
}
